﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Threading;
using Google.GData.Client;
using Google.GData.Extensions;
using Google.GData.Calendar;
using Microsoft.WindowsMobile.PocketOutlook;
using Microsoft.WindowsMobile.Status;
using System.Globalization;

namespace RareEdge
{
	public partial class frmMain : Form
	{
		#region Variables

		private string _statusText = "Ready";
		private bool _runThread = false;
		private bool _wasCancelled = false;
		private int _totalEvents = 0;
		private int _processedEvents = 0;
		private GMobileSync _gMobileSync = null;
		private OutlookSession _outlook = new OutlookSession();
		ArrayList entries = null; //The variable that holds the events returned from Google.

		#endregion

		#region Events

		public frmMain()
		{
			InitializeComponent();

		}

		private void mnuAbout_Click(object sender, EventArgs e)
		{
			using (frmAbout aboutForm = new frmAbout())
			{
				aboutForm.ShowDialog();
			}
		}

		private void mnuExit_Click(object sender, EventArgs e)
		{
			Application.Exit();
		}

		private void frmMain_KeyDown(object sender, KeyEventArgs e)
		{

		}

		private void mnuCredentials_Click(object sender, EventArgs e)
		{
			using (frmCredentials credentialsForm = new frmCredentials())
			{
				credentialsForm.ShowDialog();
			}
		}

		private void mnuSync_Click(object sender, EventArgs e)
		{
			//if (GMobileSync.IsReallyConnected())
			//{
			Thread t = new Thread(new ThreadStart(Synchronize));

			DisableInterface();

			this._statusText = "Ready";
			this.lblStatus.Text = this._statusText;
			this._totalEvents = 0;
			this._processedEvents = 0;
			this.progressBar.Value = this._processedEvents;
			this._wasCancelled = false;
			this._runThread = true;

			t.Start();
			//}
			//else
			//{
			//    MessageBox.Show("No network connection was detected. You must have access to the internet in order to use GMobileSync.",
			//        "Network Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1);
			//}
		}

		private void frmMain_Closing(object sender, CancelEventArgs e)
		{
			this._runThread = false;
		}

		private void StatusText_Update(object sender, EventArgs e)
		{
			lblStatus.Text = this._statusText;

			if (this._totalEvents > 0)
			{
				progressBar.Value = Convert.ToInt32(((1.0 * this._processedEvents) / this._totalEvents) * 100.0);
			}
			else
			{
				progressBar.Value = 0;
			}
		}

		private void Synchronize_Complete(object sender, EventArgs e)
		{
			if (this._gMobileSync != null && this._gMobileSync.ExitOnSyncComplete)
				Application.Exit();

			this.progressBar.Value = 0;

			EnableInterface();
		}

		private void mnuSettings_Click(object sender, EventArgs e)
		{
			using (frmSettings settingsForm = new frmSettings())
			{
				settingsForm.ShowDialog();
			}
		}

		private void mnuCancelSync_Click(object sender, EventArgs e)
		{
			this._runThread = false;
		}

		private void mnuDelete_Click(object sender, EventArgs e)
		{
			if (MessageBox.Show("Really remove all Google Calendar events?", "Confirm",
					MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) ==
					DialogResult.Yes)
			{
				MessageBox.Show("This may take a few moments. Please be patient!", "Information",
						MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);


				GMobileSync gms = GMobileSync.Load();

				ArrayList appointmentsToDelete = new ArrayList();

				foreach (Appointment appointment in this._outlook.Appointments.Items)
				{
					object propertyValue = GMobileSync.GetCustomProperty(appointment, "GmsWritten");

					if (propertyValue != null)
					{
						bool gmsWritten = (bool)propertyValue;

						if (gmsWritten)
							appointmentsToDelete.Add(appointment.ItemId.ToString());
					}
				}

				foreach (string itemId in appointmentsToDelete)
				{
					Appointment appointment = new Appointment(new ItemId(Convert.ToInt32(itemId)));

					appointment.Delete();
				}


				gms.SynchronizedEvents = new ArrayList();
				gms.Save();

				lblStatus.Text = "Deleted all GMobileSync events!";
			}
		}

		private void mnuHide_Click(object sender, EventArgs e)
		{
			this.Hide();
		}

		#endregion

		#region Private Methods

		private void DisableInterface()
		{
			mnuExit.Enabled = false;
			mnuAbout.Enabled = false;
			mnuSettings.Enabled = false;
			mnuCredentials.Enabled = false;
			mnuDelete.Enabled = false;
			mnuCancelSync.Enabled = true;
			mnuSync.Enabled = false;
		}

		private void EnableInterface()
		{
			mnuExit.Enabled = true;
			mnuAbout.Enabled = true;
			mnuSettings.Enabled = true;
			mnuCredentials.Enabled = true;
			mnuDelete.Enabled = true;
			mnuCancelSync.Enabled = false;
			mnuSync.Enabled = true;
		}

		private void Synchronize()
		{
			try
			{
				while (this._runThread)
				{
					this._processedEvents = 0;
					this._totalEvents = 0;

					this._statusText = "Loading settings...";
					this.Invoke(new EventHandler(StatusText_Update));

					this._gMobileSync = GMobileSync.Load();
					_gMobileSync.DownloadProgressEvent += new GMobileSync.DataEventHandler(UpdateStatusInfo);

                    if (!this._gMobileSync.IsByassNetworkTest)
                    {
                        this._statusText = "Testing network connection...";
                        this.Invoke(new EventHandler(StatusText_Update));

                        // Can GMS connect to the internet?
                        if (!this._gMobileSync.IsReallyConnected())
                        {
                            // Do you want to try anyway?
                            if (MessageBox.Show("No network connection was detected. Internet access is required. Would you like to try anyway?",
                                    "Network Error", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2) == DialogResult.No)
                            {

                                // Cancelling!
                                this._statusText = "No network connection detected.";
                                this.Invoke(new EventHandler(StatusText_Update));

                                return;
                            }

                            // Yes, try anyway, ignore the fact that there doesn't *seem* to be an internet connection.
                        }
                    }

					if (this._gMobileSync != null) // Settings are populated
					{
						this._statusText = "Downloading events...";
						this.Invoke(new EventHandler(StatusText_Update));

						// Ask Google for all events in the range
						entries = this._gMobileSync.GetGCalendarEntries();
						ArrayList updatedSynchronizedEvents = new ArrayList();
						ArrayList entriesToDelete = new ArrayList();

						if (entries != null) // Did we get any events back?
						{
							this._processedEvents = 0;
							this._totalEvents = entries.Count; // For statistics

							this._statusText = string.Format("Saving 1 of {0} events...", this._totalEvents.ToString());
							this.Invoke(new EventHandler(StatusText_Update));

							// For every event, let's start writing to pocket outlook
							foreach (EventEntry entry in entries)
							{
								if (this._runThread) // If the user has not cancelled
								{
									Appointment appointment = new Appointment();
									string body = string.Empty;
									DateTime start = new DateTime(1981, 8, 13, 14, 57, 00);
									DateTime end = new DateTime(1981, 8, 13, 14, 57, 00);
									string existingItemId = string.Empty;
									bool appointmentDeletedLocally = false;
									bool appointmentWasChangedLocally = false;
									bool appointmentWasChangedRemotely = false;

									// Figure out if this google event already exists in pocket outlook
									existingItemId = this._gMobileSync.FindPreviouslySynchronizedEvent(entry.Id.AbsoluteUri);

									try
									{
										// If the item already exists in pocket outlook, try to create it.
										// If that fails, then it was deleted locally but not on google calendar
										if (!string.IsNullOrEmpty(existingItemId))
										{
											appointment = new Appointment(new ItemId(Convert.ToInt32(existingItemId)));

											string localChecksum = appointment.Properties["GmsLocalChecksum"] as string;

											if (localChecksum != null)
											{
												appointmentWasChangedLocally = !GMobileSync.CompareChecksumEquality(appointment, localChecksum);
											}
										}
									}

									catch (Exception)
									{
										// That appointment was deleted locally but not on GCal
										appointment = new Appointment();
										existingItemId = null;
										appointmentDeletedLocally = true;
										entriesToDelete.Add(entry);
									}

									// Are there times asssociated with the event?
									if (entry.Times != null && entry.Times.Count > 0)
									{
										// Only use the earliest time because that's all that pocket outlook supports
										When when = GMobileSync.GetEventEntryEarliestTime(entry.Times);

										appointment.AllDayEvent = when.AllDay;
										start = new DateTime(when.StartTime.Year,
																				 when.StartTime.Month,
																				 when.StartTime.Day,
																				 when.StartTime.Hour,
																				 when.StartTime.Minute,
																				 when.StartTime.Second,
																				 DateTimeKind.Unspecified);
										end = new DateTime(when.EndTime.Year,
																			 when.EndTime.Month,
																			 when.EndTime.Day,
																			 when.EndTime.Hour,
																			 when.EndTime.Minute,
																			 when.EndTime.Second,
																			 DateTimeKind.Unspecified);
									}

									// Is there a body/description?
									if (entry.Content != null)
									{
										body = entry.Content.Content;
									}

									appointment.Subject = entry.Title.Text;
									appointment.Body = body;

									// Set the times
									appointment.Start = start;
									appointment.End = end;

									#region Locations

									if (entry.Locations != null)
									{
										appointment.Location = string.Empty;

										foreach (Where where in entry.Locations)
										{
											if (where != null && !string.IsNullOrEmpty(where.ValueString))
												appointment.Location += string.Format("{0}, ", where.ValueString);
										}

										// Trim the last comma space
										if (appointment.Location.Length > 2)
											appointment.Location = appointment.Location.Substring(0, appointment.Location.Length - 2);
									}

									#endregion

									#region Participants

									if (entry.Participants != null)
									{
										int recipientCount = appointment.Recipients.Count;

										// Remove all previous recipients
										for (int n = 0; n < recipientCount; n++)
											appointment.Recipients.Remove(0);

										foreach (Who who in entry.Participants)
										{
											Recipient recipient = new Recipient(who.ValueString, who.Email);

											appointment.Recipients.Add(recipient);
										}
									}

									#endregion

									#region Recurrence

									if (entry.Recurrence == null)
									{
										if (appointment.RecurrencePattern != null)
											appointment.RecurrencePattern.Clear();
									}
									else
									{
										try
										{
											string gcalRule = entry.Recurrence.Value;
											string gcalRuleStart = "RRULE:";
											string gcalRuleEnd = "\r\n";
											int gcalRuleStartPos = gcalRule.IndexOf(gcalRuleStart) +
													gcalRuleStart.Length;

											string dtstartLine = gcalRule.Substring(0, gcalRule.IndexOf("\r\n"));
											//Commented by Sampath on 27Nov2007 as there is no Duration Element in the iCal string returned by GCal
											//string durationLine = gcalRule.Substring(gcalRule.IndexOf("DURATION:"), (gcalRule.IndexOf("RRULE:") - 2) - gcalRule.IndexOf("DURATION:"));
											//Added by Sampath to on 27Nov2007 to read the DTEND element returned by GCal
											string dtendLine = gcalRule.Substring(gcalRule.IndexOf("DTEND;"), (gcalRule.IndexOf("RRULE:") - 2) - gcalRule.IndexOf("DTEND;"));

											string dtstartDateTime = dtstartLine.Substring(dtstartLine.LastIndexOf(":") + 1);
											//Commented by Sampath on 27Nov2007 as there is no Duration Element in the iCal string returned by GCal
											//string dtstartDuration = durationLine.Substring("DURATION:PT".Length, durationLine.Length - "DURATION:PT".Length - 1);
											//Added by Sampath to on 27Nov2007 to read the End Date element returned by GCal
											string dtendDateTime = dtendLine.Substring(dtendLine.LastIndexOf(":") + 1);
											string[] formats = new string[] { "r", "s", "u", "yyyyMMdd", "yyyyMMddTHHmmss" };

											appointment.Start = DateTime.ParseExact(dtstartDateTime, formats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
											appointment.End = DateTime.ParseExact(dtendDateTime, formats, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
											//Commented by Sampath on 27Nov2007 as there is no Duration Element in the iCal string returned by GCal                                            
											//appointment.Duration = new TimeSpan(0, 0, Convert.ToInt32(dtstartDuration));
											//Added by Sampath on 27Nov2007 to calculate the duration from the start and end times
											//appointment.Duration = appointment.End.Subtract(appointment.Start);

											if (gcalRuleStartPos >= 0) // Make sure we're positive
											{
                                                int gcalRuleEndPos = gcalRule.IndexOf(gcalRuleEnd, gcalRuleStartPos);
                                                if (gcalRuleEndPos > gcalRuleStartPos)
                                                    gcalRule = gcalRule.Substring(gcalRuleStartPos, gcalRuleEndPos - gcalRuleStartPos);
                                                else
                                                    gcalRule = gcalRule.Substring(gcalRuleStartPos);

												string[] rules = gcalRule.Split(';');
												bool recurrUntilForever = true;

												foreach (string rule in rules)
												{
													string property = (rule.Split('='))[0];
													string value = (rule.Split('='))[1];

													switch (property.ToUpper())
													{
														case "FREQ":
															switch (value.ToUpper())
															{
																case "DAILY":
																	appointment.RecurrencePattern.RecurrenceType = RecurrenceType.Daily;

																	break;

																case "WEEKLY":
																	appointment.RecurrencePattern.RecurrenceType = RecurrenceType.Weekly;

																	break;

																case "MONTHLY":
																	appointment.RecurrencePattern.RecurrenceType = RecurrenceType.Monthly;
																	appointment.RecurrencePattern.DayOfMonth = start.Day;

																	break;

																case "YEARLY":
																	appointment.RecurrencePattern.RecurrenceType = RecurrenceType.Yearly;
																	appointment.RecurrencePattern.DayOfMonth = start.Day;

																	switch (start.Month)
																	{
																		case 1:
																			appointment.RecurrencePattern.MonthOfYear = Month.January;

																			break;

																		case 2:
																			appointment.RecurrencePattern.MonthOfYear = Month.February;

																			break;

																		case 3:
																			appointment.RecurrencePattern.MonthOfYear = Month.March;

																			break;

																		case 4:
																			appointment.RecurrencePattern.MonthOfYear = Month.April;

																			break;

																		case 5:
																			appointment.RecurrencePattern.MonthOfYear = Month.May;

																			break;

																		case 6:
																			appointment.RecurrencePattern.MonthOfYear = Month.June;

																			break;

																		case 7:
																			appointment.RecurrencePattern.MonthOfYear = Month.July;

																			break;

																		case 8:
																			appointment.RecurrencePattern.MonthOfYear = Month.August;

																			break;

																		case 9:
																			appointment.RecurrencePattern.MonthOfYear = Month.September;

																			break;

																		case 10:
																			appointment.RecurrencePattern.MonthOfYear = Month.October;

																			break;

																		case 11:
																			appointment.RecurrencePattern.MonthOfYear = Month.November;

																			break;

																		case 12:
																			appointment.RecurrencePattern.MonthOfYear = Month.December;

																			break;
																	}

																	break;
															}

															break;

														case "BYMONTH":
															// Not supported by Google Calendar
															break;

														case "BYDAY":
															string[] weekdays = value.Split(',');

															foreach (string weekday in weekdays)
															{
																switch (weekday.ToUpper())
																{
																	case "SU":
																		appointment.RecurrencePattern.DaysOfWeekMask |= DaysOfWeek.Sunday;
																		break;

																	case "MO":
																		appointment.RecurrencePattern.DaysOfWeekMask |= DaysOfWeek.Monday;
																		break;

																	case "TU":
																		appointment.RecurrencePattern.DaysOfWeekMask |= DaysOfWeek.Tuesday;
																		break;

																	case "WE":
																		appointment.RecurrencePattern.DaysOfWeekMask |= DaysOfWeek.Wednesday;
																		break;

																	case "TH":
																		appointment.RecurrencePattern.DaysOfWeekMask |= DaysOfWeek.Thursday;
																		break;

																	case "FR":
																		appointment.RecurrencePattern.DaysOfWeekMask |= DaysOfWeek.Friday;
																		break;

																	case "SA":
																		appointment.RecurrencePattern.DaysOfWeekMask |= DaysOfWeek.Saturday;
																		break;
																}
															}

															break;

														case "INTERVAL":
															appointment.RecurrencePattern.Interval = Convert.ToInt32(value);

															break;

														case "UNTIL":
															//Modified by Sampath on 27Nov2007 as the value is of the Format UNTIL=20071207T153000Z;
															//DateTime endDuration = DateTime.ParseExact(value, "yyyyMMdd", null);
															DateTime endDuration = DateTime.ParseExact(value.Substring(0, 8), "yyyyMMdd", null);

															appointment.RecurrencePattern.PatternStartDate = start;
															appointment.RecurrencePattern.PatternEndDate = endDuration;
															//appt.RecurrencePattern.Duration = endDuration.Subtract(DateTime.Now);
															recurrUntilForever = false;

															break;

														case "WKST":
															// week start
															break;
													}
												}

												if (recurrUntilForever)
													appointment.RecurrencePattern.NoEndDate = true;
											}
										}
										catch
										{
											appointment.RecurrencePattern.Clear();
										}
									}

									#endregion

									#region Reminder

									if (entry.Reminder != null)
									{
										appointment.ReminderSet = true;
										appointment.ReminderDialog = true;
										appointment.ReminderLed = true;
										appointment.ReminderSound = true;
										appointment.ReminderMinutesBeforeStart = entry.Reminder.Minutes;
									}

									#endregion

									#region Custom properties

									// Add the GmsWritten property so we know this event was from Google
									if (!GMobileSync.SetCustomProperty(ref appointment, "GmsWritten", true, true, typeof(bool)))
									{
										// Did not add or set property
									}

									// Add the SelfUri property so we know where the event is on Google
									if (!GMobileSync.SetCustomProperty(ref appointment, "GmsSelfUri", entry.SelfUri.ToString(), true, typeof(string)))
									{
										// Did not add or set property
									}

									string checksumRemoteOriginal = string.Empty;

									if (GMobileSync.GetCustomProperty(appointment, "GmsGoogleChecksum") == null)
									{
										if (!GMobileSync.SetCustomProperty(ref appointment, "GmsGoogleChecksum", string.Empty, true, typeof(string)))
										{
											// Did not add or set property
										}
									}

									try
									{
										checksumRemoteOriginal = GMobileSync.GetCustomProperty(appointment, "GmsGoogleChecksum") as string;
										appointmentWasChangedRemotely = !GMobileSync.CompareChecksumEquality(appointment, checksumRemoteOriginal);
									}
									catch (Exception) { }

									//if (appt.Properties["GmsGoogleChecksum"] != null)
									//{
									//    checksumRemoteOriginal = appt.Properties["GmsGoogleChecksum"] as string;

									//    appointmentWasChangedRemotely = !GMobileSync.CompareChecksumEquality(appt, checksumRemoteOriginal);
									//}

									// appointment.Properties["GmsGoogleChecksum"] = GMobileSync.GenerateChecksum(appointment);

									// Add the checksum so we can tell if the event was modified locally
									if (!GMobileSync.SetCustomProperty(ref appointment, "GmsGoogleChecksum", GMobileSync.GenerateChecksum(appointment), true, typeof(string)))
									{
										// Did not add or set property
									}

									/*
									// Add the checksum so we can tell if the event was modified remotely
									if (!appointment.Properties.Contains("GmsLocalChecksum"))
									{
											appointment.Properties.Add("GmsLocalChecksum", typeof(string));
											appointment.Properties["GmsLocalChecksum"] = string.Empty;
									}

									appointment.Properties["GmsLocalChecksum"] = GMobileSync.GenerateChecksum(appointment);
									*/

									// Add the checksum so we can tell if the event was modified remotely
									if (!GMobileSync.SetCustomProperty(ref appointment, "GmsLocalChecksum", GMobileSync.GenerateChecksum(appointment), true, typeof(string)))
									{
										// Did not add or set property
									}

									#endregion

									// It's an update, the item exists locally
									if (!string.IsNullOrEmpty(existingItemId))
									{
										if (appointmentWasChangedRemotely || (!appointmentWasChangedRemotely && !appointmentWasChangedLocally))
										{
											appointment.Update(); // Uh, update!
										}
									}
									else // It's a new item
									{
										if (!appointmentDeletedLocally) // If it wasn't deleted locally
										{
											// Save for the first time
											this._outlook.Appointments.Items.Add(appointment);
										}
									}

									// Keep track of the events we've synchronized
									updatedSynchronizedEvents.Add(string.Format("{0},{1}",
											appointment.ItemId.ToString(), entry.Id.AbsoluteUri));

									// For the progress bar
									this._processedEvents++;

									// Oh baby, oh baby, update the progress bar
									if (this._processedEvents < this._totalEvents)
										this._statusText = string.Format("Saving {0} of {1} events...",
												this._processedEvents + 1, this._totalEvents.ToString());

									this.Invoke(new EventHandler(StatusText_Update));
								} // if (this._runThread)
								else
								{
									this._wasCancelled = true;

									this._statusText = "Cancelling...";
									this.Invoke(new EventHandler(StatusText_Update));

									// Okay, we cancelled so we need to fix the sync list before saving or it'll dupe events
									updatedSynchronizedEvents = this._gMobileSync.MergeSynchronizationList(updatedSynchronizedEvents);
								}
							} // for each entry
						} // if (entries != null)
						else
						{
							MessageBox.Show("An error occurred while getting events from Google Calendar. Please make sure your Google Account credentials are correct. (Your network connection seems to be fine.)",
									"Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
						}

						// Update which events have been synchronized (even if the user cancelled)
						this._gMobileSync.SynchronizedEvents = updatedSynchronizedEvents;
						this._gMobileSync.Save();

						this._statusText = "Removing deleted events from Google...";
						this.Invoke(new EventHandler(StatusText_Update));

						foreach (EventEntry entry in entriesToDelete)
						{
							this._statusText = string.Format("Removing {0} from Google...", entry.Title.Text);
							this.Invoke(new EventHandler(StatusText_Update));

							try
							{
								// Try to delete the item, if it's there. Google can be tricky :\
								entry.Delete();
							}
							catch (Exception) { }
						}

						this._statusText = "Looking for local changes...";
						this.Invoke(new EventHandler(StatusText_Update));

						SynchronizeLocalToGoogle(entries);

						//Update the last Synchronized Date Time. 
						//Added bY Sampath on 28Nov2007
						this._gMobileSync.LastSyncDateTime = DateTime.Now;

						this._runThread = false; // Cancelled or done, so we're done!

						// Choose message to display in status label
						this._statusText = this._wasCancelled ? "Synchronization cancelled." : "Synchronization complete!";
						this.Invoke(new EventHandler(StatusText_Update));
					} // if (this._gMobileSync != null)
					else
					{
						MessageBox.Show("An error occurred while reading the settings file. A synchronization cannot be started.",
								"Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
					}
				}
			}

			catch (GDataRequestException ex)
			{
				if (ex.InnerException != null &&
						ex.InnerException.Message.ToLower().Equals("The remote name could not be resolved".ToLower()))
				{
					MessageBox.Show("No network connection was detected. You must have access to the internet in order to use GMobileSync.",
							"Network Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1);

					this._statusText = "No network connection detected.";
					this.Invoke(new EventHandler(StatusText_Update));
				}
				else if (ex.Message.ToLower().Equals("Execution of authentication request failed".ToLower()))
				{
					MessageBox.Show("Your credentials are incorrect. Please check your Google Account username and password.",
							"Network Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1);

					this._statusText = "Your credentials are incorrect.";
					this.Invoke(new EventHandler(StatusText_Update));
				}
				else
				{
					MessageBox.Show("Please report this error: " + ex.Message, "Error",
							MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1);

					this._statusText = "An error occurred.";
					this.Invoke(new EventHandler(StatusText_Update));
				}
			}

			catch (Exception ex)
			{
				MessageBox.Show("Please report this error: " + ex.Message, "Error",
						MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1);

				this._statusText = "An error occurred.";
				this.Invoke(new EventHandler(StatusText_Update));
			}

			finally
			{
				this._runThread = false;
				this.Invoke(new EventHandler(Synchronize_Complete));
			}
		}

		private void SynchronizeLocalToGoogle(ArrayList entries)
		{
			ArrayList appointmentsToBeAdded = new ArrayList(); //Appointments to be Added to GCal
			ArrayList appointmentsToBeUpdated = new ArrayList(); //Appointments to be Updated in GCal
			//Added by Sampath on 27Nov2007 to handle Appointments to Delete Locally
			ArrayList appointmentsToDelete = new ArrayList();
			EventEntry[] entriesToBeAdded = null;
			int index = 0;

			if (this._gMobileSync == null)
				this._gMobileSync = GMobileSync.Load();

			foreach (Appointment appointment in this._outlook.Appointments.Items)
			{

				if ((appointment.Start >= DateTime.Now.AddDays(-1 * this._gMobileSync.SyncPreviousDays) &&
						appointment.End <= DateTime.Now.AddDays(this._gMobileSync.SyncFutureDays)) || appointment.IsRecurring)
				{

					//-----------------------------------------------------------------------------------------------//
					//Added by Sampath on 28Nov2007 to sync Recurring appointments whose start and end dates are beyond
					//the synchronization period, but where there is an occurance within the synchronization period.
					if (appointment.IsRecurring)
					{
                        DateTime oldStart = appointment.Start;
                        DateTime oldEnd = appointment.End;
                   
						DateTime start = DateTime.Now.AddDays(-1 * this._gMobileSync.SyncPreviousDays);
						DateTime end = DateTime.Now.AddDays(this._gMobileSync.SyncFutureDays);
						DateTime temp = start;
						bool foundOccurance = false;
						while ((temp <= end) && !foundOccurance)
						{
							if (appointment.RecurrencePattern.GetOccurrence(temp) != null)
                            {
                                appointment.Start = oldStart;
                                appointment.End = oldEnd;
								foundOccurance = true;
                            }
							else
								temp = temp.AddDays(1);
						}
						if (!foundOccurance)
							continue;
					}
					//-----------------------------------------------------------------------------------------------//

					bool appointmentWasChangedLocally = false;
					bool appointmentIsBrandNew = true;

					// Local entries that aren't new should have a checksum
					string originalLocal = GMobileSync.GetCustomProperty(appointment, "GmsLocalChecksum") as string;

					if (originalLocal != null)
					{
						appointmentWasChangedLocally = !GMobileSync.CompareChecksumEquality(appointment, originalLocal);
					}

					// Check to see if it's a brand new appointment all together
					if (GMobileSync.GetCustomProperty(appointment, "GmsWritten") != null)
					{
						appointmentIsBrandNew = !(bool)GMobileSync.GetCustomProperty(appointment, "GmsWritten");
					}

					if (appointmentIsBrandNew && !appointmentWasChangedLocally)
					{
						appointmentsToBeAdded.Add(appointment.ItemId.ToString());
					}
					//Added by Sampath on 27Nov2007 to handle local appointments that need to get deleted because they have been deleted from GCal
					else
					{
						//Check whether the appointment exists on GCal.
						// If it doesnt, then it needs to be deleted from Outlook
						string selfUri = null;
						//string selfUri = appointment.Properties.Contains("GmsSelfUri") ?
						//appointment.Properties["GmsSelfUri"].ToString() : string.Empty;
						if (GMobileSync.GetCustomProperty(appointment, "GmsSelfUri") != null)
						{
							selfUri = (string)GMobileSync.GetCustomProperty(appointment, "GmsSelfUri");
						}

						if (!string.IsNullOrEmpty(selfUri))
						{

                            // Check if this item was update right now
                            bool isJustUpdated = false;
                            foreach (EventEntry entry in entries)
                            {
                                if (entry.SelfUri == selfUri)
                                {
                                    isJustUpdated = true;
                                    break;
                                }
                            }
                            if ((isJustUpdated) && (appointmentWasChangedLocally))
                            {
                                appointmentsToBeUpdated.Add(appointment.ItemId.ToString());
                            }
                            else if (!isJustUpdated)
                            {
							    try
							    {
								    EventEntry entry = this._gMobileSync.GetEvent(selfUri);
								    if (entry == null)
									    appointmentsToDelete.Add(appointment.ItemId.ToString());
							    }
							    catch
							    {
								    //Exception occurd because the event for the given Uri cannot be found on Google
								    appointmentsToDelete.Add(appointment.ItemId.ToString());

							    }
                            }
						}
                    }
                    this._statusText = string.Format("Found {0} local updates...", appointmentsToBeAdded.Count + appointmentsToBeUpdated.Count + appointmentsToDelete.Count);
                    this.Invoke(new EventHandler(StatusText_Update));
                }
            }

			//MessageBox.Show("I'm going to move " + appointmentsToBeAdded.Count + " appointments to Google!");
			//MessageBox.Show("I'm going to update " + appointmentsToBeUpdated.Count + " appointments on Google!");

			entriesToBeAdded = new EventEntry[appointmentsToBeAdded.Count];

			int toBeAddedCounter = 0;

			foreach (string itemId in appointmentsToBeAdded)
			{
				Appointment appointment = new Appointment(new ItemId(Convert.ToInt32(itemId)));
				DateTime startTime = new DateTime(appointment.Start.Year,
																					appointment.Start.Month,
																					appointment.Start.Day,
																					appointment.Start.Hour,
																					appointment.Start.Minute,
																					appointment.Start.Second,
																					DateTimeKind.Unspecified);
				DateTime endTime = new DateTime(appointment.End.Year,
																				appointment.End.Month,
																				appointment.End.Day,
																				appointment.End.Hour,
																				appointment.End.Minute,
																				appointment.End.Second,
																				DateTimeKind.Unspecified);

				entriesToBeAdded[index] = this._gMobileSync.CreateEvent(appointment.Subject,
						this._gMobileSync.Username, this._gMobileSync.Username,
						startTime,
						endTime,
						appointment.AllDayEvent,
						appointment.Location, appointment.Body);

				if (appointment.RecurrencePattern.RecurrenceType != RecurrenceType.NoRecurrence)
				{
					entriesToBeAdded[index].Recurrence = new Google.GData.Extensions.Recurrence();
					entriesToBeAdded[index].Recurrence.Value = GMobileSync.CreateRecurrenceRule(appointment, this._gMobileSync.Timezone);
				}
				else
				{
					entriesToBeAdded[index].Recurrence = null;
				}

				#region Custom properties

				// Update custom properties

				if (!GMobileSync.SetCustomProperty(ref appointment, "GmsWritten", true, true, typeof(bool)))
				{
					// Could not set or create property
				}

				// Add the checksum so we can tell if the event was modified remotely
				if (!GMobileSync.SetCustomProperty(ref appointment, "GmsLocalChecksum", GMobileSync.GenerateChecksum(appointment), true, typeof(string)))
				{
					// Could not set or create property
				}

				// Add the checksum so we can tell if the event was modified locally
				if (!GMobileSync.SetCustomProperty(ref appointment, "GmsGoogleChecksum", GMobileSync.GenerateChecksum(appointment), true, typeof(string)))
				{
					// Could not set or create property
				}

				#endregion

				// Quick and dirty workaround to avoid "Native Method Call Fail"
                appointment.Start = startTime;
                appointment.End = endTime;
				appointment.Update();

				index++;
				toBeAddedCounter++;

				this._statusText = string.Format("Adding to Google {0} of {1}...", toBeAddedCounter, appointmentsToBeAdded.Count);
				this.Invoke(new EventHandler(StatusText_Update));
			}

			int toBeUpdatedCounter = 0;

			foreach (string itemId in appointmentsToBeUpdated)
			{
				// Get the local appointment
				Appointment appointment = new Appointment(new ItemId(Convert.ToInt32(itemId)));

				// Get the remote appointment and then update it
				string selfUri = appointment.Properties.Contains("GmsSelfUri") ?
						appointment.Properties["GmsSelfUri"].ToString() : string.Empty;

				if (!string.IsNullOrEmpty(selfUri))
				{
					EventEntry entry = this._gMobileSync.GetEvent(selfUri);
					Where location = new Where();
					When when = new When();

					entry.Title = new AtomTextConstruct(AtomTextConstructElementType.Title, appointment.Subject);
					entry.Published = DateTime.Now;
					entry.Updated = DateTime.Now;

					location.ValueString = appointment.Location;

					entry.Locations.Clear();
					entry.Locations.Add(location);

					when.AllDay = appointment.AllDayEvent;
					when.StartTime = new DateTime(appointment.Start.Year,
																				appointment.Start.Month,
																				appointment.Start.Day,
																				appointment.Start.Hour,
																				appointment.Start.Minute,
																				appointment.Start.Second,
																				DateTimeKind.Unspecified);
					when.EndTime = new DateTime(appointment.End.Year,
																				appointment.End.Month,
																				appointment.End.Day,
																				appointment.End.Hour,
																				appointment.End.Minute,
																				appointment.End.Second,
																				DateTimeKind.Unspecified);

					entry.Times.Clear();
					entry.Times.Add(when);

					entry.Participants.Clear();

					foreach (Recipient recipient in appointment.Recipients)
					{
						Who participant = new Who();

						participant.ValueString = recipient.Name;
						participant.Email = recipient.Address;
						if (recipient.Name == AppointmentProperty.MeetingOrganizerName.ToString())
							participant.Rel = Who.RelType.EVENT_ORGANIZER;
						else
							participant.Rel = Who.RelType.EVENT_ATTENDEE;

						entry.Participants.Add(participant);
					}

					if (appointment.RecurrencePattern.RecurrenceType != RecurrenceType.NoRecurrence)
					{
						entry.Recurrence = new Google.GData.Extensions.Recurrence();
						entry.Recurrence.Value = GMobileSync.CreateRecurrenceRule(appointment, this._gMobileSync.Timezone);
					}
					else
					{
						entry.Recurrence = null;
					}

					if (appointment.Categories != "")
					{
						string[] categories = appointment.Categories.Split(',');
						foreach (string category in categories)
						{
							entry.Categories.Add(new AtomCategory(category));
						}
					}

					entry = entry.Update() as EventEntry; // To get the new SelfUri

					#region Custom properties

					// Update custom properties

					if (!GMobileSync.SetCustomProperty(ref appointment, "GmsWritten", true, true, typeof(bool)))
					{
						// Could not add or update property
					}

					// Add the SelfUri so we can track this mofo
					if (!GMobileSync.SetCustomProperty(ref appointment, "GmsSelfUri", entry.SelfUri.ToString(), true, typeof(string)))
					{
						// Could not add or update property
					}

					// Add the checksum so we can tell if the event was modified remotely
					if (!GMobileSync.SetCustomProperty(ref appointment, "GmsLocalChecksum", GMobileSync.GenerateChecksum(appointment), true, typeof(string)))
					{
						// Could not add or update property
					}

					// Add the checksum so we can tell if the event was modified locally
					if (!GMobileSync.SetCustomProperty(ref appointment, "GmsGoogleChecksum", GMobileSync.GenerateChecksum(appointment), true, typeof(string)))
					{
						// Could not add or update property
					}

					#endregion

					try
					{
						// Quick and dirty workaround to avoid possible "Native Method Call Fail"
                        appointment.Start = new DateTime(appointment.Start.Year,
                                                        appointment.Start.Month,
                                                        appointment.Start.Day,
                                                        appointment.Start.Hour,
                                                        appointment.Start.Minute,
                                                        appointment.Start.Second,
                                                        DateTimeKind.Unspecified);
                        appointment.End = new DateTime(appointment.End.Year,
                                                        appointment.End.Month,
                                                        appointment.End.Day,
                                                        appointment.End.Hour,
                                                        appointment.End.Minute,
                                                        appointment.End.Second,
                                                        DateTimeKind.Unspecified);
						appointment.Update();
					}
					catch
					{
					}

					toBeUpdatedCounter++;

					this._statusText = string.Format("Updating on Google {0} of {1}...", toBeUpdatedCounter, appointmentsToBeUpdated.Count);
					this.Invoke(new EventHandler(StatusText_Update));
				}
				else
				{
					Console.Write("We skipped an event becase it didn't have a SelfUri.");

					if (!GMobileSync.SetCustomProperty(ref appointment, "GmsWritten", false, true, typeof(bool)))
					{
						// Could not add or update property
					}

					// Add the SelfUri so we can track this mofo
					if (!GMobileSync.SetCustomProperty(ref appointment, "GmsSelfUri", string.Empty, true, typeof(string)))
					{
						// Could not add or update property
					}

					// Add the checksum so we can tell if the event was modified remotely
					if (!GMobileSync.SetCustomProperty(ref appointment, "GmsLocalChecksum", string.Empty, true, typeof(string)))
					{
						// Could not add or update property
					}

					// Add the checksum so we can tell if the event was modified locally
					if (!GMobileSync.SetCustomProperty(ref appointment, "GmsGoogleChecksum", string.Empty, true, typeof(string)))
					{
						// Could not add or update property
					}
				}
			}

			int toBeDeletedCounter = 0;
			foreach (string itemId in appointmentsToDelete)
			{
				// Get the local appointment
				Appointment appointment = new Appointment(new ItemId(Convert.ToInt32(itemId)));

				//Delete the local appointment
				try
				{
					appointment.Delete();
				}
				catch (Exception) { }

				toBeDeletedCounter++;

				this._statusText = string.Format("Removing from Local {0} of {1}...", toBeDeletedCounter, appointmentsToDelete.Count);
				this.Invoke(new EventHandler(StatusText_Update));
			}


			if (this._gMobileSync == null)
				this._gMobileSync = GMobileSync.Load();

            if (entriesToBeAdded.Length != 0)
                entriesToBeAdded = this._gMobileSync.InsertEvents(entriesToBeAdded);

			ArrayList updatedSynchronizedEvents = new ArrayList();

			this._statusText = "Saving synchronization state...";
			this.Invoke(new EventHandler(StatusText_Update));

			for (int n = 0; n < entriesToBeAdded.Length; n++)
			{
				EventEntry entry = entriesToBeAdded[n];
				// To avoid "Null Reference" exception
				if (entry == null)
					continue;
				Appointment appointment = new Appointment(new ItemId(Convert.ToInt32(appointmentsToBeAdded[n])));

				// Add the SelfUri property so we know where the event is on Google
				if (!GMobileSync.SetCustomProperty(ref appointment, "GmsSelfUri", entry.SelfUri.ToString(), true, typeof(string)))
				{
					// Could not add or update property
				}

				updatedSynchronizedEvents.Add(string.Format("{0},{1}",
						appointment.ItemId.ToString(), entry.Id.AbsoluteUri));
				
				// Quick and dirty workaround to avoid "Native Method Call Fail"
                appointment.Start = new DateTime(appointment.Start.Year,
                                                appointment.Start.Month,
                                                appointment.Start.Day,
                                                appointment.Start.Hour,
                                                appointment.Start.Minute,
                                                appointment.Start.Second,
                                                DateTimeKind.Unspecified);
                appointment.End = new DateTime(appointment.End.Year,
                                            appointment.End.Month,
                                            appointment.End.Day,
                                            appointment.End.Hour,
                                            appointment.End.Minute,
                                            appointment.End.Second,
                                            DateTimeKind.Unspecified);
				appointment.Update(); // Save the SelfUri for Google

				this._statusText = string.Format("Saving state {0} of {1}", n + 1, entriesToBeAdded.Length);
				this.Invoke(new EventHandler(StatusText_Update));
			}

			updatedSynchronizedEvents = this._gMobileSync.MergeSynchronizationList(updatedSynchronizedEvents);
			this._gMobileSync.SynchronizedEvents = updatedSynchronizedEvents;
			this._gMobileSync.Save();
		}

		void UpdateStatusInfo(GMobileSync sender, string text, int processed, int total)
		{
			this._processedEvents = processed;
			this._totalEvents = total;

			this._statusText = text;
			this.Invoke(new EventHandler(StatusText_Update));
		}

		#endregion
	}
}